#include "zlog.h"
#include "client_sq.h"
#include "temp.h"

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <getopt.h>
#include <libgen.h>
#include <linux/tcp.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <signal.h>
#include <time.h>

#define IPADDR          "127.0.0.1"       //默认IP
#define PORT             8999             //默认port
#define CLIENTID        "00001"           //默认主机号
#define INTERVAL_TIME    10                //默认采样间隔时间


void sig_handle(int signum);
void handle_pipe(int sig);
int socketconnected(int sockfd);
int connect_server(int port,char *ipaddr);
int package(struct client_data a,char *buf);
static inline void print_help(char *progname);
int             g_stop = 0;           //信号


int main(int argc,char **argv)
{
	char                   progname[20];
	int                    cli_socket_fd = -1;        
	int                    cli_connect_fd = -1;        
	struct sockaddr_in     cli_addr;

	int                    opt;
	int                    daemon_run = 0;
	int                    port;
	int                    interval_time;
	char                   ipaddr[20];    //ip地址

	char                   buf1[50];        
	char                   buf2[50];
	int                    rc;             //zlog
	zlog_category_t       *c = NULL;              //zlog

	sqlite3               *db = NULL;
	int                    temp_id = -1;   //备份的记录编号        
	struct client_data     cli_data;        
	float                  cli_temp;        
	int                    rv;        
	char                   cli_time[20];  
	char                   client_id[20];

	time_t                 now;
	int                    last_time = 0;
	int                    sample_flag = 0;
	int                    connect_flag = 0;

	/*--------------------------------*
	 *        命令行参数解析          *
	 *--------------------------------*/
	struct option long_options[] = {       
		{"daemon",no_argument,NULL,'b'},
		{"IDADDR",required_argument,NULL,'i'},
		{"PORT",required_argument,NULL,'p'},
		{"interval_time",required_argument,NULL,'t'},
		{"client_id",required_argument,NULL,'n'},
		{"help",no_argument,NULL,'h'},
		{"NULL",0,NULL,0}
	};

	//命令行参数初始化
	memset(progname,0,sizeof(progname));
	memset(ipaddr,0,sizeof(ipaddr));
	memset(client_id,0,sizeof(client_id));
	memset(cli_time,0,sizeof(cli_time));

	//默认参数信息
	port = PORT;
	interval_time = INTERVAL_TIME;
	strcpy(progname,basename(argv[0]));
	strcpy(ipaddr,IPADDR);
	strcpy(client_id,CLIENTID);

	while((opt = getopt_long(argc,argv,"bi:p:t:n:h",long_options,NULL))!= -1)
	{
		switch(opt)
		{
			case 'b':
				daemon_run = 1;
				break;
			case 'i':
				strcpy(ipaddr,optarg);
				break;
			case 'p':
				port = atoi(optarg);
				break;
			case 't':
				interval_time = atoi(optarg);
				break;
			case 'n':
				strcpy(client_id,optarg);
				break;
			case 'h':
				print_help(progname);
				return EXIT_SUCCESS;
			default :
				break;
		}
	}

	//后台
	if(daemon_run)
	{
		daemon(1,1);
	}

	//zlog
	rc = zlog_init("./client.conf");
	if(rc) 
	{
		printf("init failed\n");
		return -1;
	}       	
	c = zlog_get_category("my_cat");
	if(!c)
	{
		printf("Get cat failure\n");
		zlog_fini();
		return -2;
	}

	//打开数据库,创建表
	db = create_database();
	if(!db)
	{
		zlog_error(c,"Can't open database:%s",strerror(errno));
		return -3;
	}
	printf("Opened database successfully\n");

	//安装信号
	signal(SIGTERM,sig_handle);
	signal(SIGPIPE,&handle_pipe);

	//主循环
	while(!g_stop)
	{

		time(&now);
		sample_flag = 0;

		/*--------------------------------*	 
		 *            获取温度            *	
		 *--------------------------------*/
		if( now - last_time >= interval_time )
		{
			if(get_time(cli_time,sizeof(cli_time)) < 0)
			{
				zlog_error(c,"Get time failure:%s",strerror(errno));
				continue;
			}
			if(get_temperature(&cli_temp) < 0)
			{			
				zlog_error(c,"Get temperature failure:%s",strerror(errno));
				continue;
			}
			sample_flag = 1;
			last_time = now;

			//将获取的温度、时间、客户端编号打包进结构体
			memset(&cli_data,0,sizeof(cli_data));	       	
			memset(buf1,0,sizeof(buf1));	       	
			strcpy(cli_data.id,client_id);
			strcpy(cli_data.time,cli_time);
			cli_data.temp = cli_temp;

			//将数据打包为字符串
			if(package(cli_data,buf1) < 0)
			{
				zlog_error(c,"Package failure:%s",strerror(errno));
				continue;
			}
		}

		/*--------------------------------*	 
		 *            建立连接            *	
		 *--------------------------------*/
		if( !connect_flag )
		{
			cli_socket_fd = connect_server(port,ipaddr);
			if(cli_socket_fd > 0)
			{
				//printf("%d\n",cli_socket_fd);
				connect_flag = 1;  //已经连接上
				zlog_info(c,"Connect to server successfully!");
			}
		}

		//检测连接状态
		if(socketconnected(cli_socket_fd) < 0)
		{
			if(sample_flag)
			{
				//备份数据
				zlog_notice(c,"Socket get disconnected");
				rc = insert_temp(buf1,db);
				if(rc < 0)
				{
					printf("%d\n",rc);
					zlog_error(c,"Back up data failure:%s",strerror(errno));
				}
				else
				{
					zlog_notice(c,"Back up datas to table CLIENT_TEMP");
				}
			}
			connect_flag = 0;
			close(cli_socket_fd);
			continue;
		}

		/*--------------------------------*	 
		 *         发送新获得的数据       *	
		 *--------------------------------*/
		if( sample_flag )
		{
			//发送新获得的数据
			if(write(cli_socket_fd,buf1,sizeof(buf1)) < 0 )
			{
				zlog_notice(c,"Socket get disconnected");			       	
				if(insert_temp(buf1,db) < 0)
				{
					zlog_error(c,"Back up data failure:%s",strerror(errno));
					continue;
				}
				zlog_notice(c,"Back up datas to table CLIENT_TEMP");
				connect_flag = 0;
				close(cli_socket_fd);
				continue;
			}
			printf("Sent data to server successfully:%s\n",buf1);
			zlog_info(c,"Sent temperature to server successfully");
		}

		/*--------------------------------*	 
		 *         发送表中的数据         *	
		 *--------------------------------*/
		memset(buf2,0,sizeof(buf2));
		temp_id = selfirst_temp(buf2,db);
		//有数据
		if(temp_id >= 0)
		{				

			//发送表中的第一条数据
			if(write(cli_socket_fd,buf2,sizeof(buf2)) < 0)                       
			{
				zlog_error(c,"Socket get disconnected");
				connect_flag = 0;
				close(cli_socket_fd);
				continue;
			}
			printf("The data int the database is sent to server successfully:%s\n",buf2);
			zlog_info(c,"The data in the database is sent to server successfully");
			if(delete_temp(temp_id,db) < 0)
			{
				zlog_error(c,"Delete data failure:%s",strerror(errno));
				continue;
			}
		}
	}

	//程序退出提示
	zlog_info(c,"Power off client:%s",CLIENTID);
	printf("Power off client\n");
	sqlite3_close(db);
	close(cli_socket_fd);
	zlog_fini();
	return 0;
}

/**************************************************
 * 函数名：   sig_handle
 *
 * 函数功能： 将g_stop设为1，结束while循环使主函数退出
 *
 * 参数：     signum          信号
 *
 * 返回值:    无返回值
 **************************************************/
void sig_handle(int signum)
{
	printf("Catch signal [%d]\n",signum);
	g_stop = 1;
}

/**************************************************
 * 函数名：   handle_pipe
 *
 * 函数功能： 忽略SIGPIPE信号
 *
 * 参数：     sig           信号
 *
 * 返回值：   无返回值
 **************************************************/
void handle_pipe(int sig)
{
	//do nothing
}

/**************************************************
 * 函数名：   connect_server
 *
 * 函数功能： 创建socket套接字，连接服务器
 *
 * 参数：     port       端口
 *            ipaddr     IP地址
 *
 * 返回值：   成功返回0，失败返回负数
 **************************************************/
int connect_server(int port,char *ipaddr)
{
	int                    cli_socket_fd;
	int                    cli_connect_fd;
	struct sockaddr_in     cli_addr;
	int                    opt = 1;

	if(port <= 0 || !ipaddr)
	{
		return -1;
	}

	cli_socket_fd = socket(AF_INET,SOCK_STREAM,0);
	if(cli_socket_fd < 0)
	{
		printf("Create client failure:%s\n",strerror(errno));
		return -2;
	}

	memset(&cli_addr,0,sizeof(cli_addr));
	cli_addr.sin_family = AF_INET;
	cli_addr.sin_port = htons(port);
	inet_aton(ipaddr,&cli_addr.sin_addr);
	if(setsockopt(cli_socket_fd,SOL_SOCKET,SO_KEEPALIVE,(char *)&opt,sizeof(opt)) < 0)
	{
		printf("setsockopt failure:%s",strerror(errno));
		close(cli_socket_fd);
		return -3;
	}

	cli_connect_fd = connect(cli_socket_fd,(struct sockaddr *)&cli_addr,sizeof(cli_addr));
	if(cli_connect_fd < 0)
	{
		close(cli_socket_fd);
		return -4;
	}
	return cli_socket_fd;
}

/**************************************************
 * 函数名：   package
 *
 * 函数功能： 将要发送的数据打包
 *
 * 参数：     a          存储数据的结构体
 *            buf        打包后的数据
 *
 * 返回值：   成功返回0，输入参数有误返回-1
 **************************************************/
int package(struct client_data a,char *buf)
{
	if(!buf)
	{
		return -1;
	}
	sprintf(buf,"%s%s%f%s%s\n",a.id,SEPARATOR,a.temp,SEPARATOR,a.time);
	return 0;
}

/**************************************************
 * 函数名：   socketconnected
 *
 * 函数功能： 检测socket连接状态
 *
 * 参数：     sockfd       套接字
 *
 * 返回值：   正常连接返回0，否则返回-1
 **************************************************/
int socketconnected(int sockfd)
{
	struct tcp_info info;         //使用tcp_info结构体的tcpi_state成员变量来存取socket的连接状态，
	int len = sizeof(info);       //如果此返回值为1，则说明socket连接正常，如果返回值为0，则说明连接异常。
	if (sockfd <= 0)
	{
		return -1;
	}

	//getsockopt()函数进行判断客户端和服务器端的连接状态，
	getsockopt(sockfd, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *) & len);
	if((info.tcpi_state == 1)) 
	{
		//printf("socket connected\n");
		return 0;
	}
	else 
	{
		//printf("socket disconnected\n");
		return -1;
	}
}

/**************************************************
 * 函数名：   printf_help
 *
 * 函数功能： 打印帮助信息
 *
 * 参数：     progname        功能名
 *
 * 返回值：   无返回值
 **************************************************/
static inline void print_help(char *progname)
{
	printf("\n*******************************************Help******************************************\n");
	printf("USAGE:%s [option]...\n",progname);
	printf("%s is a client program,which can be used to sent temperature and time to the sever\n",progname);
	printf("\nMandatory argument to long option are mandatory for short options too:\n");
	printf("-b[daemon]    set program running on background\n");
	printf("-i[IDADDR]    set the IP address of the client prognam\n");
	printf("-p[PORT]      set the PORT of the client progname\n");
	printf("-t[time]      set the time delay of the client progname\n");
	printf("-n[client_id] set the client ID of the client progname\n");
	printf("-h[help]      Display this help information\n");

	printf("\nExample:%s -b -p 8888\n",progname);
	printf("*****************************************************************************************\n");
	printf("\n");

	return;
}
